package com.ftl.gateway.api.v2.filter;

import com.alibaba.fastjson.JSONObject;
import com.ftl.gateway.api.v2.common.manager.RestTemplateManager;
import com.ftl.gateway.api.v2.common.utils.StringUtil;
import com.ftl.gateway.api.v2.common.utils.StringUtils;
import com.ftl.gateway.api.v2.filter.dto.AuthCheckReturnDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Set;

/**
 * @author : wangweifeng
 * @date : Created in 2022/8/8 15:30
 */
@Component
@Slf4j
public class AuthFilter implements GlobalFilter, Ordered {

    @Value("${allowed.paths}")
    private String paths; // 不需要登录就能访问的路径

    @Autowired
    private RestTemplateManager restTemplate;

    @Value("${jwt.tokenHeader}")
    private String tokenHeader;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("=========================请求进入filter=========================");

        ServerHttpRequest request = exchange.getRequest();
        String requestPath = request.getPath().toString();

        log.info("predicate path: {}", requestPath);

        boolean allowedPath = false;
        if (paths != null && !paths.equals("")){
            allowedPath = StringUtil.checkSkipAuthUrls(requestPath, paths.split(","));
        }
        if (allowedPath || StringUtils.isEmpty(requestPath)){
            return chain.filter(exchange);
        }

//        String token = request.getHeaders().getFirst(tokenHeader);
//        if (StringUtils.isBlank(token)) {
//            log.info("predicate path: {}, comment: {}", requestPath, "Please Login");
//            return writeResponse(exchange.getResponse(), 500, "Please Login");
//        }
        log.error("new headers: {}", request.getHeaders());
        log.error("new getPath:{}", request.getPath());
        log.error("new getMethod:{}", request.getMethod());
        log.error("new getURI:{}", request.getURI());
        log.error("new getCookies:{}", request.getCookies());
        log.error("new getQueryParams:{}", request.getQueryParams());
        // 请求AuthServer
        AuthCheckReturnDTO result = restTemplate.getFromAuth(request);
        if (!result.getSuccess()) {
            log.error("Auth Check Error writing response: {}", result.getMessage());
            return writeResponse(exchange.getResponse(), result.getStatus(), result.getMessage(), requestPath);
        }
        // 获取请求中的filter[client][in][]
        List<String> filterClientIds = request.getQueryParams().get("filter[client][in][]");
        Set<String> clientIds = result.getParam().get("filter[client][in][]");
        if (filterClientIds != null && clientIds != null) {
            for (String client : filterClientIds) {
                if (!clientIds.contains(client)) {
                    log.info("predicate path: {}, comment: {}", requestPath, HttpStatus.FORBIDDEN.getReasonPhrase());
                    return writeResponse(exchange.getResponse(), HttpStatus.FORBIDDEN, HttpStatus.FORBIDDEN.getReasonPhrase(), requestPath);
                }
            }
        }
        // 赋值
        URI uri = request.getURI();
        StringBuilder query = new StringBuilder();
        String originalQuery = uri.getRawQuery();
        if (org.springframework.util.StringUtils.hasText(originalQuery)) {
            query.append(originalQuery);
            if (originalQuery.charAt(originalQuery.length() - 1) != '&') {
                query.append('&');
            }
        }
        if (CollectionUtils.isEmpty(filterClientIds)) {
            for (String key : result.getParam().keySet()) {
                for (String param : result.getParam().get(key)) {
                    String value = ServerWebExchangeUtils.expand(exchange, param);
                    query.append(URLEncoder.encode(key));
                    query.append('=');
                    query.append(value);
                    query.append('&');
                }
            }
        }
        URI newUri = UriComponentsBuilder.fromUri(uri).replaceQuery(query.toString()).build(true).toUri();
        request = exchange.getRequest().mutate().headers((httpHeaders) -> {
            for (String key : result.getHeader().keySet()) {
                httpHeaders.set(key, result.getHeader().get(key));
            }
        }).uri(newUri).build();
        log.info("new uri: {}", newUri);
        log.info("new headers: {}", request.getHeaders());
        log.info("new getPath:{}", request.getPath());
        log.info("new getMethod:{}", request.getMethod());
        log.info("new getURI:{}", request.getURI());
        log.info("new getCookies:{}", request.getCookies());
        log.info("new getQueryParams:{}", request.getQueryParams());
        return chain.filter(exchange.mutate().request(request).build());
    }

    /**
     * 值越小执行顺序越靠前
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }

    /**
     * 构建返回内容
     *
     * @param response ServerHttpResponse
     * @param status     返回码
     * @param msg     返回数据
     * @return Mono
     */
    protected Mono<Void> writeResponse(ServerHttpResponse response, HttpStatus status, String msg, String path) {
        JSONObject message = new JSONObject();
        message.put("status", status.value());
        message.put("message", msg);
        message.put("path", path);
        byte[] bits = message.toJSONString().getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = response.bufferFactory().wrap(bits);
        response.setStatusCode(status);
        // 指定编码，否则在浏览器中会中文乱码
        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        return response.writeWith(Mono.just(buffer));
    }
}
